package com.zy.explore.generator.infrastructure.core.service;

import cn.hutool.core.date.DateUtil;
import com.zy.explore.generator.infrastructure.constant.ProjectConstant;
import com.zy.explore.generator.infrastructure.core.brick.GenerationController;
import com.zy.explore.generator.infrastructure.core.brick.GenerationFrm;
import com.zy.explore.generator.infrastructure.core.brick.GenerationRouter;
import com.zy.explore.generator.infrastructure.core.brick.GenerationVO;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationAssembler;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationFacade;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationFeign;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationFeignFallback;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationPersistenceLayer;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationService;
import com.zy.explore.generator.infrastructure.core.brick.parent.GenerationValidator;
import com.zy.explore.generator.infrastructure.core.enums.FrameworkTypeEnum;
import com.zy.explore.generator.infrastructure.core.frm.BrickInfoFrm;
import com.zy.explore.generator.infrastructure.core.frm.DbManageFrm;
import com.zy.explore.generator.infrastructure.core.util.DbHelper;
import com.zy.explore.generator.infrastructure.core.util.StrUtil;
import com.zy.explore.generator.infrastructure.core.vo.TableColumnVO;
import com.zy.explore.generator.infrastructure.core.vo.TableVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * description 表代码生成实现类
 *
 * @author 🇨🇳中国印🇨🇳
 * @date 2021年06月05日 14:03
 */
@Slf4j
@Service
public class TableGeneratorService {

    @Resource
    private GenerationFrm generationFrm;

    @Resource
    private GenerationVO generationVO;

    @Resource
    private GenerationRouter generationRouter;

    @Resource
    private GenerationFeign generationFeign;

    @Resource
    private GenerationFeignFallback generationFeignFallback;

    @Resource
    private GenerationFacade generationFacade;

    @Resource
    private GenerationAssembler generationAssembler;

    @Resource
    private GenerationValidator generationValidator;

    @Resource
    private GenerationController generationController;

    @Resource
    private GenerationService generationService;

    @Resource
    private GenerationPersistenceLayer generationPersistenceLayer;

    /**
     * 根据具体指定的表生成代码
     *
     * @param frm 参数对象
     * @throws Exception 异常
     */
    public void genTableCode(BrickInfoFrm frm) throws Exception {
        // 初始化默认值
        this.initDefault(frm);

        List<String> tableNameList = frm.getTableNameList();
        for (String tableName : tableNameList) {
            // 设置表名
            frm.setTableName(tableName);
            // 设置表名对应的Java属性名称
            frm.setPropTableName(StrUtil.underlineToHump(tableName));
            // 设置表相关的类名前缀
            frm.setTableClassNamePrefix(StrUtil.underlineToHumpFirstUpperCase(StrUtil.removePrefix(tableName, new String[]{frm.getTableNamePrefix()})));
            // 表信息
            TableVO tableVO = DbHelper.getTableInfo(DbManageFrm.builder()
                    .mysqlHost(frm.getMysqlHost()).mysqlPort(frm.getMysqlPort()).mysqlDb(frm.getMysqlDb())
                    .mysqlUser(frm.getMysqlUser()).mysqlPwd(frm.getMysqlPwd())
                    .tableName(tableName)
                    .build());
            if (tableVO == null) {
                log.info("数据库{}中无此表{}信息", frm.getMysqlUrl(), tableName);
                throw new Exception("Unknown table " + tableName);
            }
            //查询列信息
            List<TableColumnVO> columns = DbHelper.getColumnInfo(frm);
            if (CollectionUtils.isEmpty(columns)) {
                log.info("数据库{}中的表{}无列信息", frm.getMysqlUrl(), tableName);
                continue;
            }
            List<String> columnNameList = columns.stream().map(TableColumnVO::getColumnName).collect(Collectors.toList());
            String allColumns = "`" + StringUtils.join(columnNameList, "`,`") + "`";
            // 设置模板中所需的所有列信息
            frm.setAllColumns(allColumns);
            // 生成代码
            this.generator(frm, tableVO, columns);
        }
    }

    /**
     * 根据不同的架构生成对应的砖块
     *
     * @param frm     参数
     * @param tableVO 表信息
     * @param columns 列信息
     */
    private void generator(BrickInfoFrm frm, TableVO tableVO, List<TableColumnVO> columns) throws Exception {
        frm.setTableVO(tableVO);
        frm.setTableColumns(columns);

        FrameworkTypeEnum frameworkType = frm.getFrameworkType();
        this.setPrefix(frm);
        if (frameworkType == FrameworkTypeEnum.DDD || frameworkType == FrameworkTypeEnum.DDD_FEIGN) {
            this.generateDddBricks(frm);
        } else {
            throw new Exception("未匹配到架构类型");
        }
    }

    /**
     * 生成DDD架构的砖块
     *
     * @param frm 参数
     */
    private void generateDddBricks(BrickInfoFrm frm) throws Exception {
        // 1.生成Frm
        generationFrm.doGeneration(frm);
        // 2.生成VO
        generationVO.doGeneration(frm);
        // 3.生成Router
        // 先填充字段
        frm.setTableNameUpperCaseUnderline(StrUtil.humpToUnderline(frm.getTableName()).toUpperCase());
        generationRouter.doGeneration(frm);
        if (frm.getFrameworkType() == FrameworkTypeEnum.DDD_FEIGN) {
            // 3.1生成Feign
            generationFeign.doGeneration(frm);
            // 3.2生成FeignFallback
            // generationFeignFallback.doGeneration(frm);
        }
        // 4.生成Facade
        generationFacade.doGeneration(frm);
        // 5.生成Assembler
        generationAssembler.doGeneration(frm);
        // 6.生成Validator
        generationValidator.doGeneration(frm);
        // 7.生成Service
        generationService.doGeneration(frm);
        // 8.生成持久层
        generationPersistenceLayer.doGeneration(frm);
    }


    /**
     * 生成MVC架构的砖块
     *
     * @param frm 参数
     */
    private void generateMvcBricks(BrickInfoFrm frm) throws Exception {
        // 1.生成Frm
        generationFrm.doGeneration(frm);
        // 2.生成VO
        generationVO.doGeneration(frm);
        // 3.生成Assembler
        generationAssembler.doGeneration(frm);
        // 4.生成Controller
        generationController.doGeneration(frm);
        // 5.生成Service
        generationService.doGeneration(frm);
        // 6.生成JPA持久层
        generationPersistenceLayer.doGeneration(frm);
    }

    /**
     * 初始化一些默认设置
     *
     * @param frm 入参
     */
    private void initDefault(BrickInfoFrm frm) {
        List<FrameworkTypeEnum> frameworkTypeEnums = Arrays.asList(FrameworkTypeEnum.DDD, FrameworkTypeEnum.DDD_FEIGN);
        if (!frameworkTypeEnums.contains(frm.getFrameworkType())) {
            frm.setFrameworkType(FrameworkTypeEnum.DDD_FEIGN);
        }
        if (org.springframework.util.StringUtils.isEmpty(frm.getBasePackageName())) {
            frm.setBasePackageName("com.zy.explore.demo");
        }
        if (org.springframework.util.StringUtils.isEmpty(frm.getAuthorName())) {
            frm.setAuthorName("🇨🇳中国印🇨🇳");
        }
        if (org.springframework.util.StringUtils.isEmpty(frm.getTableNamePrefix())) {
            frm.setTableNamePrefix("t_");
        }
        if (frm.getCommon() == null) {
            frm.setCommon(false);
        }
        setPrefix(frm);
        // 设置生成的代码位置
        if (StringUtils.isBlank(frm.getCodeRoot())) {
            // 直接数据库表逆向生成代码就放在code包下
            frm.setCodeRoot("code");
        }
        if (frm.getFrameworkType() == FrameworkTypeEnum.DDD_FEIGN) {
            // 如果是DDD_FEIGN项目，则设置feign、provider的代码位置
            frm.setFeignCodeRoot(frm.getCodeRoot() + "/" + frm.getProjectName() + "/" + frm.getProjectName() + "-feign"
                    + ProjectConstant.SRC_MAIN_JAVA + "/" + frm.getBasePackageName().replace(".", "/"));
            frm.setProviderCodeRoot(frm.getCodeRoot() + "/" + frm.getProjectName() + "/" + frm.getProjectName() + "-provider"
                    + ProjectConstant.SRC_MAIN_JAVA + "/" + frm.getBasePackageName().replace(".", "/"));
        } else {
            frm.setProviderCodeRoot(frm.getCodeRoot() + "/" + frm.getProjectName() + ProjectConstant.SRC_MAIN_JAVA + "/" + frm.getBasePackageName().replace(".", "/"));
        }
        // 设置时间
        frm.setDateTime(DateUtil.format(new Date(), "yyyy年MM月dd日 HH:mm:ss"));
    }

    private void setPrefix(BrickInfoFrm frm) {
        // 持久层使用jpa、tk.mybatis、mybatis-plus中的任意两个或以上，则设置对应的前缀，并设置不能去前缀
        if (frm.getUseJpa() && frm.getUseTkMybatis() && !frm.getUseMybatisPlus()) {
            frm.setPrefixJpa("Jpa").setPrefixTk("Tk").setPrefixMp("").setTrimPrefix(false);
        } else if (frm.getUseJpa() && !frm.getUseTkMybatis() && frm.getUseMybatisPlus()) {
            frm.setPrefixJpa("Jpa").setPrefixTk("").setPrefixMp("Mp").setTrimPrefix(false);
        } else if (!frm.getUseJpa() && frm.getUseTkMybatis() && frm.getUseMybatisPlus()) {
            frm.setPrefixJpa("").setPrefixTk("Tk").setPrefixMp("Mp").setTrimPrefix(false);
        } else if (frm.getUseJpa() && frm.getUseTkMybatis() && frm.getUseMybatisPlus()) {
            frm.setPrefixJpa("Jpa").setPrefixTk("Tk").setPrefixMp("Mp").setTrimPrefix(false);
        } else {
            // 否则，持久层用jpa、tk.mybatis、mybatis-plus中的两个以下或不用，则不设置前缀，且设置去前缀
            frm.setPrefixJpa("").setPrefixTk("").setPrefixMp("").setTrimPrefix(true);
        }
    }

}
